package demo;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;

public class DemoApi {
	public static final String WAY_HTTPCLIENT = "httpclient";
	public static final String WAY_JAVANET = "java.net";

	private String appkey = "";
	private String secretcode = "";
	private String UrlPre = "";
	private String appkeyField = "";
	private boolean isHttps = false;

	private HttpClient client;

	public DemoApi(String urlPre, String appkey, String secretcode, String appkeyField) {
		this.UrlPre = urlPre;
		this.appkey = appkey;
		this.appkeyField = appkeyField;
		this.secretcode = secretcode;
		this.isHttps = urlPre.startsWith("https");
	}

	/**
	 * 对请求参数集进行MD5签名
	 * 
	 * @param params     待签名的请求参数集
	 * @param secretCode 签名密码
	 * @return 返回null 或 32位16进制大写字符串
	 */
	public static String signMD5(Map<String, String> params, String secretCode) {
		if (params == null || params.isEmpty())
			return "";
		if (params instanceof java.util.TreeMap) {
			return signMD5Inner((TreeMap<String, String>) params, secretCode);
		} else {
			TreeMap<String, String> treeMap = new TreeMap<>();
			treeMap.putAll(params);
			return signMD5Inner(treeMap, secretCode);
		}
	}

	/**
	 * 对请求参数集进行MD5签名
	 * 
	 * @param param      待签名的请求参数集
	 * @param secretCode 签名密码
	 * @return 返回32位16进制大写字符串
	 */
	private static String signMD5Inner(TreeMap<String, String> param, String secretCode) {
		String singStr = signStr(param, secretCode, false);
		try {
			byte[] unencodedPassword = singStr.getBytes("utf-8");
			MessageDigest md = null;
			try {
				md = MessageDigest.getInstance("md5");
			} catch (Exception e) {
				return null;
			}
			md.reset();
			md.update(unencodedPassword);
			byte[] encodedPassword = md.digest();

			return Hex.encodeHexString(encodedPassword);
		} catch (UnsupportedEncodingException e) {
			return null;
		}
	}

	/**
	 * 将请求参数按key=value&key=valuesecretCode拼接 <br/>
	 * 排除key为sign和signmethod的key-value
	 * 
	 * @param param      请求参数
	 * @param secretCode 签名密码
	 * @return
	 */
	public static String signStr(TreeMap<String, String> param, String secretCode, boolean startAppend) {
		StringBuilder orgin = new StringBuilder();
		String value = "";
		for (String name : param.keySet()) {
			// 参与签名的值不包括参数中的签名值和签名方法
			if (!StringUtils.equalsIgnoreCase(name, "sign") && !StringUtils.equalsIgnoreCase(name, "signmethod")) {
				value = param.get(name);
				if (StringUtils.isEmpty(value)) {
					value = "";
				}
				orgin.append(name).append("=").append(value).append("&");
			}
		}
		if (startAppend) {
			return secretCode + StringUtils.substringBeforeLast(orgin.toString(), "&");
		}
		return StringUtils.substringBeforeLast(orgin.toString(), "&") + secretCode;
	}

	private static String getCurTimestamp() {
		return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
	}

	private HttpClient createHttpClient() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
		if (client != null) {
			return client;
		}
		if (isHttps) {
			SSLContext sslContext = SSLContexts.custom().useSSL().build();
			sslContext.init(null, new X509TrustManager[] { new EmptyX509TrustManager() }, new SecureRandom());
			SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslContext,
					SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
			HttpClientBuilder builder = HttpClients.custom().setSSLSocketFactory(factory);
			builder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
			client = builder.build();
		} else {
			client = HttpClientBuilder.create().build();
		}
		return client;
	}

	// 借助apache httpclient快捷实现请求
	protected void handleByHttpclient(String url, Map<String, String> params) throws Exception {
		HttpClient client = createHttpClient();
		HttpPost post = new HttpPost(url);
		List<NameValuePair> urlParameters = new ArrayList<>();
		for (String k : params.keySet()) {
			urlParameters.add(new BasicNameValuePair(k, params.get(k)));
		}
		post.setEntity(new UrlEncodedFormEntity(urlParameters, Consts.UTF_8));
		System.out.println("way1: " + url + ", parameters : " + urlParameters);
		HttpResponse response = client.execute(post);

		// 返回信息展示
		System.out.println("Response Code : " + response.getStatusLine().getStatusCode());
		BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
		StringBuffer result = new StringBuffer();
		String line = "";
		while ((line = rd.readLine()) != null) {
			result.append(line);
		}
		System.out.println("Response Content : " + result.toString());
	}

	// 使用java.net基础方式实现请求(针对Https请求)
	private static void handleByJavaNet4Https(String url, Map<String, String> params) throws Exception {
		URL obj = new URL(url);
		HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
		con.setRequestMethod("POST");

		StringBuilder urlParameters = new StringBuilder();
		String value = "";
		for (String name : params.keySet()) {
			value = params.get(name);
			if (StringUtils.isEmpty(value)) {
				value = "";
			}
			value = URLEncoder.encode(value, "utf-8");
			urlParameters.append(name).append("=").append(value).append("&");
		}
		con.setRequestProperty("Accept-Charset", "utf-8");
		con.setRequestProperty("contentType", "utf-8");

		SSLContext sc = SSLContext.getInstance("SSL", "SunJSSE");
		sc.init(null, new X509TrustManager[] { new EmptyX509TrustManager() }, new SecureRandom());
		HostnameVerifier hv = new HostnameVerifier() {
			public boolean verify(String arg0, javax.net.ssl.SSLSession arg1) {
				return true;
			}
		};
		con.setDefaultHostnameVerifier(hv);
		con.setDefaultSSLSocketFactory(sc.getSocketFactory());

		con.setDoOutput(true);
		DataOutputStream wr = new DataOutputStream(con.getOutputStream());
		wr.writeBytes(urlParameters.toString());
		wr.flush();
		wr.close();

		int responseCode = con.getResponseCode();
		System.out.println("way2:parameters : " + urlParameters);
		System.out.println("Response Code : " + responseCode);
		BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
		String inputLine;
		StringBuffer response = new StringBuffer();

		while ((inputLine = in.readLine()) != null) {
			response.append(inputLine);
		}
		in.close();
		System.out.println("Response Content : " + response.toString());
	}

	// 使用java.net基础方式实现请求
	private static void handleByJavaNet(String url, Map<String, String> params) throws Exception {
		URL obj = new URL(url);
		HttpURLConnection con = (HttpURLConnection) obj.openConnection();
		con.setRequestMethod("POST");

		StringBuilder urlParameters = new StringBuilder();
		String value = "";
		for (String name : params.keySet()) {
			value = params.get(name);
			if (StringUtils.isEmpty(value)) {
				value = "";
			}
			value = URLEncoder.encode(value, "utf-8");
			urlParameters.append(name).append("=").append(value).append("&");
		}
		con.setRequestProperty("Accept-Charset", "utf-8");
		con.setRequestProperty("contentType", "utf-8");

		con.setDoOutput(true);
		DataOutputStream wr = new DataOutputStream(con.getOutputStream());
		wr.writeBytes(urlParameters.toString());
		wr.flush();
		wr.close();

		int responseCode = con.getResponseCode();
		System.out.println("way2:parameters : " + urlParameters);
		System.out.println("Response Code : " + responseCode);
		BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
		String inputLine;
		StringBuffer response = new StringBuffer();

		while ((inputLine = in.readLine()) != null) {
			response.append(inputLine);
		}
		in.close();
		System.out.println("Response Content : " + response.toString());
	}

	public void req(String path, Map params2, String way) {
		final String url = UrlPre + path;
		Map params = new HashMap();
		// 在此处添加业务级参数(params)
		params.putAll(params2);
		try {
			// 添加系统级参数
			params.put(appkeyField, appkey);
			params.put("v", "1.0");
			params.put("timestamp", getCurTimestamp()); // yyyy-MM-dd HH:mm:ss

			String sign = signMD5(params, secretcode);
			params.put("sign", sign);

			if (WAY_HTTPCLIENT.equals(way)) {
				handleByHttpclient(url, params);
			} else {
				if (isHttps) {
					handleByJavaNet4Https(url, params);
				} else {
					handleByJavaNet(url, params);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
